<template>
  <div class="identify-code-container">
    <canvas
      id="identify-code"
      :width="contentWidth"
      :height="contentHeight"
    ></canvas>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator";

// 组件的名称
@Component({
  name: "SIdentify",
})
// 组件对外暴露的方式class后面是组件的名称
export default class SIdentify extends Vue {
  // 这里是组件的属性，也就是组件的对外暴露的属性，外界可修改
  @Prop({ type: String, default: "1234" }) identifyCode!: string;
  // 字体的大小变化
  @Prop({ type: Number, default: 25 }) fontSizeMin!: number;
  @Prop({ type: Number, default: 30 }) fontSizeMax!: number;
  // 背景颜色的变化
  @Prop({ type: Number, default: 255 }) backgroundColorMin!: number;
  @Prop({ type: Number, default: 255 }) backgroundColorMax!: number;
  // 字体颜色的变化
  @Prop({ type: Number, default: 0 }) colorMin!: number;
  @Prop({ type: Number, default: 160 }) colorMax!: number;
  // 干扰线颜色的变化
  @Prop({ type: Number, default: 100 }) lineColorMin!: number;
  @Prop({ type: Number, default: 255 }) lineColorMax!: number;
  // 干扰点颜色的变化
  @Prop({ type: Number, default: 0 }) dotColorMin!: number;
  @Prop({ type: Number, default: 255 }) dotColorMax!: number;
  // 验证码的宽高
  @Prop({ type: Number, default: 120 }) contentWidth!: number;
  @Prop({ type: Number, default: 40 }) contentHeight!: number;

  private mounted() {
    this.drawPicture();
  }

  // 监听identifyCode的变化
  @Watch("identifyCode")
  private onIdentifyCodeChange() {
    this.drawPicture();
  }

  // 生成随机数,用于RGB颜色中的R、G、B值，以及干扰线和干扰点的位置，旋转角度
  private randomNum(min: number, max: number): number {
    return Math.floor(Math.random() * (max - min) + min);
  }

  // 生成随机颜色
  private randomColor(min: number, max: number): string {
    const r = this.randomNum(min, max);
    const g = this.randomNum(min, max);
    const b = this.randomNum(min, max);
    return `rgb(${r}, ${g}, ${b})`;
  }

  // 绘制单个字符
  private drawCharacter(ctx: CanvasRenderingContext2D, txt: string, i: number) {
    // 字体颜色
    ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax);
    // 字体大小
    ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + "px SimHei";
    const x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1));
    // 验证码范围内的y坐标
    const y = this.randomNum(this.fontSizeMax, this.contentHeight - 5);
    // 随机旋转角度
    const deg = this.randomNum(-45, 45);
    // 修改坐标原点和旋转角度
    ctx.translate(x, y);
    ctx.rotate((deg * Math.PI) / 180);
    ctx.fillText(txt, 0, 0);
    // 恢复坐标原点和旋转角度
    ctx.rotate((-deg * Math.PI) / 180);
    ctx.translate(-x, -y);
  }

  // 绘制干扰线
  private drawLine(ctx: CanvasRenderingContext2D): void {
    // 绘制多条干扰线
    for (let i = 0; i < 8; i++) {
      // 干扰线的颜色
      ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax);
      // 起点坐标
      ctx.beginPath();
      ctx.moveTo(
        this.randomNum(0, this.contentWidth),
        this.randomNum(0, this.contentHeight)
      );
      // 终点坐标
      ctx.lineTo(
        this.randomNum(0, this.contentWidth),
        this.randomNum(0, this.contentHeight)
      );
      ctx.stroke();
    }
  }

  // 绘制干扰点
  private drawDot(ctx: CanvasRenderingContext2D): void {
    // 绘制多个干扰点
    for (let i = 0; i < 30; i++) {
      // 干扰点的颜色
      ctx.fillStyle = this.randomColor(this.dotColorMin, this.dotColorMax);
      // 干扰点的位置
      ctx.beginPath();
      // 设置圆心的位置
      // 半径大小为1，起始角度为0，结束角度为2π就是完整的圆
      ctx.arc(
        this.randomNum(0, this.contentWidth),
        this.randomNum(0, this.contentHeight),
        1,
        0,
        2 * Math.PI
      );
      // 填充颜色
      ctx.fill();
    }
  }

  // 绘制验证码
  private drawPicture(): void {
    // 获取canvas元素断言为HTMLCanvasElement类型
    let canvas = document.getElementById("identify-code") as HTMLCanvasElement;
    const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
    ctx.textBaseline = "bottom";

    // 绘制背景
    // ctx.fillStyle = this.randomColor(
    //   this.backgroundColorMin,
    //   this.backgroundColorMax
    // );
    ctx.fillStyle = "#F7F5FD";
    ctx.fillRect(0, 0, this.contentWidth, this.contentHeight);

    // 绘制文字
    for (let i = 0; i < this.identifyCode.length; i++) {
      this.drawCharacter(ctx, this.identifyCode[i], i);
    }

    // 绘制干扰线
    this.drawLine(ctx);

    // 绘制干扰点
    this.drawDot(ctx);
  }
}
</script>

<style lang="scss" scoped>
.identify-code-container {
  height: 45px;
  display: flex;
  justify-content: center;
  align-items: center;
  #identify-code {
    border-radius: 5px;
  }
}
</style>